#include "stdafx.h"
#include "GraphicsEngine.h"
#define M_PI 3.14159265358979323846
GraphicsEngine::GraphicsEngine(){
}
GraphicsEngine::GraphicsEngine(HWND hWnd)
{
	
	RECT desktop;
	const HWND hDesktop = GetDesktopWindow();
	// Get the size of screen to the variable desktop
	GetWindowRect(hDesktop, &desktop);
	// The top left corner will have coordinates (0,0)
	// and the bottom right corner will have coordinates
	// (horizontal, vertical)

	width = desktop.right;
	height = desktop.bottom;

	//Setup OpenGL;
	oGW = new OpenGLWrapper();
	oGW->init(hWnd);
	hDC = GetDC(hWnd);
	setupShaderProgram();
	//setupDepthBuffer();
	//MatrixID = glGetUniformLocation(programObject, "MVP");
	glViewport(0,0,width,height);
	ShowCursor(false);
}
void GraphicsEngine::setupShaderProgram(){
	//Setup shader details
	GLubyte * my_fragment_shader_source;
	GLubyte * my_vertex_shader_source;
	unsigned long* fragSourceLen;
	unsigned long* vertSourceLen;
	fragSourceLen = new unsigned long(2);
	vertSourceLen = new unsigned long(2);


	//Get Vertex And Fragment Shader Sources
	GLenum my_program;
	GLenum my_vertex_shader;
	GLenum my_fragment_shader;
 
	// Create Shader And Program Objects
	my_program = glCreateProgramObjectARB();

	
	int j = loadshader("VertexShader.vert",my_vertex_shader_source, vertSourceLen);
	my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);	
	glShaderSource(my_vertex_shader, 1, (const GLcharARB **)&my_vertex_shader_source,NULL);
	glCompileShaderARB(my_vertex_shader);
	glAttachObjectARB(my_program, my_vertex_shader);
	

	
	int i = loadshader("FragmentShader.frag",my_fragment_shader_source, fragSourceLen);
	my_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	glShaderSource(my_fragment_shader, 1, (const GLcharARB **)&my_fragment_shader_source, NULL);
	glCompileShaderARB(my_fragment_shader);
	glAttachObjectARB(	my_program, my_fragment_shader);
	
	glLinkProgramARB(my_program);
	programObject = my_program;
}
void GraphicsEngine::setupDepthBuffer(){
		//Setup shadow-buffer
	GLenum FBOstatus;
	
	// Try to use a texture depth component
	glGenTextures(1, &depthTextureId);
	glBindTexture(GL_TEXTURE_2D, depthTextureId);

	// GL_LINEAR does not make sense for depth texture. However, next tutorial shows usage of GL_LINEAR and PCF
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	// Remove artifact on the edges of the shadowmap
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );

	// No need to force GL_DEPTH_COMPONENT24, drivers usually give you the max precision if available
	glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width*2, height*2, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
	glBindTexture(GL_TEXTURE_2D, 0);

	// create a framebuffer object
	glGenFramebuffersEXT(1, &fboId);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);

	// Instruct openGL that we won't bind a color texture with the currently bound FBO
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
 
	// attach the texture to FBO depth attachment point
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, depthTextureId, 0);

	// check FBO status
	FBOstatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	if(FBOstatus != GL_FRAMEBUFFER_COMPLETE_EXT)
		printf("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");

	// switch back to window-system-provided framebuffer
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		
	//shadowMapUniform = glGetUniformLocationARB(programObject,"ShadowMap");
}
unsigned long GraphicsEngine::getFileLength(ifstream& file){    
	if(!file.good()) 
		return 0;       
	unsigned long pos=file.tellg();    
	file.seekg(0,ios::end);   
	unsigned long len = file.tellg(); 
	file.seekg(ios::beg);      
	return len;
}
int GraphicsEngine::loadshader(char* filename, GLubyte*& ShaderSource, unsigned long*& len){   
	ifstream file;   
	file.open(filename, ios::in); // opens as ASCII!   
	if(!file){
		return -1;    
	}
	*len = getFileLength(file);      
	if (len==0) 
		return -2;   // Error: Empty File        
	ShaderSource = (GLubyte*) new char[*len+1];   
	if (*ShaderSource == 0) 
		return -3;   
	// can't reserve memory   
    // len isn't always strlen cause some characters are stripped in ascii read...
    // it is important to 0-terminate the real length later, len is just max possible value...   
	ShaderSource[*len] = 0;    
	unsigned int i=0;   
	while (file.good())   
	{     
		ShaderSource[i] = file.get();       
		// get character from file.      
		if (!file.eof())       
			i++;   }       
	ShaderSource[i] = 0;  // 0-terminate it at the correct position      
	file.close();        
	return 0; // No Error
}
void GraphicsEngine::unloadshader(GLubyte** ShaderSource)
{
   if (*ShaderSource != 0)
     delete[] *ShaderSource;
   *ShaderSource = 0;
}
void GraphicsEngine::setViewer(Viewer* view){
	v = view;
}
void GraphicsEngine::addDrawable(Drawable *d){
	drawables.push_back(d);
}
void GraphicsEngine::addLightSource(LightSource *s){
	lightSources.push_back(s);
}

void GraphicsEngine::setProjectionMatrix(float fov, float zNear, float zFar){
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(fov, (float) width/height, zNear, zFar);
}



LRESULT GraphicsEngine::handle(Window &wnd, UINT message, HWND hwnd, long param0, long param1){
	glCullFace(GL_BACK);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		
	setProjectionMatrix(90.0f, 0.01f, 5000.0f);
	v->setViewMatrix();
	
	glUseProgramObjectARB(programObject);
	for (auto it = drawables.begin(); it != drawables.end(); it++){
		(*it)->draw();
	}
	SwapBuffers(hDC);
	return 0;
}

void GraphicsEngine::startTranslate(float x,float y,float z)
{
	glPushMatrix();
	glTranslatef(x,y,z);
	
	glMatrixMode(GL_TEXTURE);
	glActiveTextureARB(GL_TEXTURE7);
	glPushMatrix();
	glTranslatef(x,y,z);
}

void GraphicsEngine::endTranslate()
{
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}
void GraphicsEngine::setTextureMatrix(void)
{
	static double modelView[16];
	static double projection[16];
	
	// This is matrix transform every coordinate x,y,z
	// x = x* 0.5 + 0.5 
	// y = y* 0.5 + 0.5 
	// z = z* 0.5 + 0.5 
	// Moving from unit cube [-1,1] to [0,1]  
	const GLdouble bias[16] = {	
		0.5, 0.0, 0.0, 0.0, 
		0.0, 0.5, 0.0, 0.0,
		0.0, 0.0, 0.5, 0.0,
		0.5, 0.5, 0.5, 1.0};
	
	// Grab modelview and transformation matrices
	glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	
	
	glMatrixMode(GL_TEXTURE);
	glActiveTextureARB(GL_TEXTURE7);
	
	glLoadIdentity();	
	glLoadMatrixd(bias);
	
	// concatating all matrice into one.
	glMultMatrixd (projection);
	glMultMatrixd (modelView);
	
	// Go back to normal matrix mode
	glMatrixMode(GL_MODELVIEW);
}

GraphicsEngine::~GraphicsEngine(void)
{
}

